#include "dataexporter.h"
#include <QFile>
#include <QTextStream>
#include <QDir>
#include <QTimer>
#include <QUuid>
#include <QDebug>
#include <QXmlStreamWriter>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QRegularExpression>
#include <QCryptographicHash>
#include <QProcess>
#include <QStandardPaths>

DataExporter::DataExporter(QObject *parent)
    : QObject(parent)
    , m_auditLogger(nullptr)
    , m_currentProgress(0)
    , m_currentStatus("Ready")
{
}

bool DataExporter::exportData(const ExportConfig &config)
{
    QString errorMessage;
    if (!validateExportConfig(config, errorMessage)) {
        emit exportError(m_currentExportId, errorMessage);
        return false;
    }
    
    m_currentExportId = generateExportId();
    emit exportStarted(m_currentExportId);
    
    setStatus("Loading audit records...");
    updateProgress(10);
    
    // Get audit records based on filter
    QVector<AuditLogger::AuditRecord> allRecords;
    if (m_auditLogger) {
        allRecords = m_auditLogger->queryEvents(
            config.filter.userIds.isEmpty() ? QString() : config.filter.userIds.first(),
            config.filter.startDate,
            config.filter.endDate
        );
    }
    
    updateProgress(30);
    
    // Apply additional filtering
    QVector<AuditLogger::AuditRecord> filteredRecords = filterRecords(allRecords, config.filter);
    
    setStatus("Exporting data...");
    updateProgress(50);
    
    bool success = false;
    
    // Export based on format
    switch (config.format) {
        case ExportFormat::CSV:
            success = exportToCSV(config.outputPath, filteredRecords, config);
            break;
        case ExportFormat::JSON:
            success = exportToJSON(config.outputPath, filteredRecords, config);
            break;
        case ExportFormat::XML:
            success = exportToXML(config.outputPath, filteredRecords, config);
            break;
        case ExportFormat::PDF:
            success = exportToPDF(config.outputPath, filteredRecords, config);
            break;
        case ExportFormat::EXCEL:
            success = exportToExcel(config.outputPath, filteredRecords, config);
            break;
        case ExportFormat::HTML:
            success = exportToHTML(config.outputPath, filteredRecords, config);
            break;
    }
    
    updateProgress(80);
    
    if (success) {
        // Post-processing
        if (config.compressOutput) {
            setStatus("Compressing output...");
            success = compressFile(config.outputPath);
        }
        
        if (success && config.encryptOutput && !config.encryptionPassword.isEmpty()) {
            setStatus("Encrypting output...");
            success = encryptFile(config.outputPath, config.encryptionPassword);
        }
    }
    
    updateProgress(100);
    setStatus(success ? "Export completed" : "Export failed");
    
    emit exportCompleted(m_currentExportId, success, config.outputPath);
    return success;
}

bool DataExporter::exportToCSV(const QString &filePath, const QVector<AuditLogger::AuditRecord> &records, 
                              const ExportConfig &config)
{
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qWarning() << "Failed to open CSV file for writing:" << filePath;
        return false;
    }
    
    QTextStream out(&file);
    out.setCodec("UTF-8");
    
    // Write headers
    if (config.includeHeaders) {
        QStringList headers;
        if (config.selectedColumns.isEmpty()) {
            headers << "Timestamp" << "User ID" << "Action" << "Table Name" 
                   << "Record ID" << "Old Value" << "New Value" << "Description";
        } else {
            headers = config.selectedColumns;
        }
        
        for (int i = 0; i < headers.size(); ++i) {
            if (i > 0) out << ",";
            out << escapeCSVField(headers[i]);
        }
        out << "\n";
    }
    
    // Write data
    for (const auto &record : records) {
        QStringList values;
        if (config.selectedColumns.isEmpty()) {
            values << formatDateTime(record.timestamp)
                  << escapeCSVField(record.userId)
                  << escapeCSVField(AuditLogger::actionToString(record.action))
                  << escapeCSVField(record.tableName)
                  << escapeCSVField(record.recordId)
                  << escapeCSVField(record.oldValue)
                  << escapeCSVField(record.newValue)
                  << escapeCSVField(record.description);
        } else {
            // Custom column selection logic would go here
            for (const QString &column : config.selectedColumns) {
                if (column == "Timestamp") values << formatDateTime(record.timestamp);
                else if (column == "User ID") values << escapeCSVField(record.userId);
                else if (column == "Action") values << escapeCSVField(AuditLogger::actionToString(record.action));
                else if (column == "Table Name") values << escapeCSVField(record.tableName);
                else if (column == "Record ID") values << escapeCSVField(record.recordId);
                else if (column == "Old Value") values << escapeCSVField(record.oldValue);
                else if (column == "New Value") values << escapeCSVField(record.newValue);
                else if (column == "Description") values << escapeCSVField(record.description);
                else values << "";
            }
        }
        
        out << values.join(",") << "\n";
    }
    
    return true;
}

bool DataExporter::exportToJSON(const QString &filePath, const QVector<AuditLogger::AuditRecord> &records, 
                               const ExportConfig &config)
{
    QJsonArray jsonArray;
    
    for (const auto &record : records) {
        jsonArray.append(recordToJson(record));
    }
    
    QJsonObject rootObject;
    rootObject["export_info"] = QJsonObject{
        {"timestamp", QDateTime::currentDateTime().toString(Qt::ISODate)},
        {"record_count", records.size()},
        {"format", "JSON"},
        {"filter_applied", !config.filter.searchText.isEmpty()}
    };
    rootObject["audit_records"] = jsonArray;
    
    QJsonDocument doc(rootObject);
    
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly)) {
        return false;
    }
    
    file.write(doc.toJson());
    return true;
}

bool DataExporter::exportToXML(const QString &filePath, const QVector<AuditLogger::AuditRecord> &records, 
                              const ExportConfig &config)
{
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly)) {
        return false;
    }
    
    QXmlStreamWriter xml(&file);
    xml.setAutoFormatting(true);
    xml.writeStartDocument();
    
    xml.writeStartElement("AuditTrailExport");
    xml.writeAttribute("timestamp", QDateTime::currentDateTime().toString(Qt::ISODate));
    xml.writeAttribute("recordCount", QString::number(records.size()));
    
    xml.writeStartElement("ExportInfo");
    xml.writeTextElement("Format", "XML");
    xml.writeTextElement("ExportDate", QDateTime::currentDateTime().toString(Qt::ISODate));
    xml.writeTextElement("RecordCount", QString::number(records.size()));
    xml.writeEndElement(); // ExportInfo
    
    xml.writeStartElement("AuditRecords");
    
    for (const auto &record : records) {
        xml.writeStartElement("Record");
        xml.writeTextElement("Timestamp", formatDateTime(record.timestamp));
        xml.writeTextElement("UserId", record.userId);
        xml.writeTextElement("Action", AuditLogger::actionToString(record.action));
        xml.writeTextElement("TableName", record.tableName);
        xml.writeTextElement("RecordId", record.recordId);
        xml.writeTextElement("OldValue", record.oldValue);
        xml.writeTextElement("NewValue", record.newValue);
        xml.writeTextElement("Description", record.description);
        xml.writeEndElement(); // Record
    }
    
    xml.writeEndElement(); // AuditRecords
    xml.writeEndElement(); // AuditTrailExport
    xml.writeEndDocument();
    
    return true;
}

bool DataExporter::exportToPDF(const QString &filePath, const QVector<AuditLogger::AuditRecord> &records, 
                              const ExportConfig &config)
{
    // For PDF export, we'll create HTML first and then convert to PDF
    QString htmlPath = filePath + ".tmp.html";
    
    if (!exportToHTML(htmlPath, records, config)) {
        return false;
    }
    
    // Use system tools to convert HTML to PDF (requires wkhtmltopdf or similar)
    QProcess process;
    QStringList arguments;
    arguments << htmlPath << filePath;
    
    process.start("wkhtmltopdf", arguments);
    process.waitForFinished(30000); // 30 second timeout
    
    // Clean up temporary HTML file
    QFile::remove(htmlPath);
    
    return process.exitCode() == 0;
}

bool DataExporter::exportToExcel(const QString &filePath, const QVector<AuditLogger::AuditRecord> &records, 
                                const ExportConfig &config)
{
    // For Excel export, we'll create a CSV file with .xlsx extension
    // In a real implementation, you'd use a library like QXlsx
    QString csvPath = filePath + ".csv";
    
    if (!exportToCSV(csvPath, records, config)) {
        return false;
    }
    
    // Rename to xlsx (simplified approach)
    return QFile::rename(csvPath, filePath);
}

bool DataExporter::exportToHTML(const QString &filePath, const QVector<AuditLogger::AuditRecord> &records, 
                               const ExportConfig &config)
{
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        return false;
    }
    
    QTextStream out(&file);
    out.setCodec("UTF-8");
    
    // HTML header
    out << "<!DOCTYPE html>\n";
    out << "<html>\n<head>\n";
    out << "<meta charset=\"UTF-8\">\n";
    out << "<title>Audit Trail Export</title>\n";
    out << "<style>\n";
    out << "body { font-family: Arial, sans-serif; margin: 20px; }\n";
    out << "table { border-collapse: collapse; width: 100%; }\n";
    out << "th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n";
    out << "th { background-color: #f2f2f2; }\n";
    out << "tr:nth-child(even) { background-color: #f9f9f9; }\n";
    out << ".export-info { margin-bottom: 20px; padding: 10px; background-color: #e9e9e9; }\n";
    out << "</style>\n";
    out << "</head>\n<body>\n";
    
    // Export information
    out << "<div class=\"export-info\">\n";
    out << "<h2>Audit Trail Export</h2>\n";
    out << "<p><strong>Export Date:</strong> " << QDateTime::currentDateTime().toString() << "</p>\n";
    out << "<p><strong>Total Records:</strong> " << records.size() << "</p>\n";
    out << "<p><strong>Format:</strong> HTML</p>\n";
    out << "</div>\n";
    
    // Table
    out << "<table>\n";
    out << "<thead>\n<tr>\n";
    out << "<th>Timestamp</th><th>User ID</th><th>Action</th><th>Table Name</th>";
    out << "<th>Record ID</th><th>Old Value</th><th>New Value</th><th>Description</th>\n";
    out << "</tr>\n</thead>\n";
    out << "<tbody>\n";
    
    for (const auto &record : records) {
        out << "<tr>\n";
        out << "<td>" << formatDateTime(record.timestamp) << "</td>\n";
        out << "<td>" << record.userId << "</td>\n";
        out << "<td>" << AuditLogger::actionToString(record.action) << "</td>\n";
        out << "<td>" << record.tableName << "</td>\n";
        out << "<td>" << record.recordId << "</td>\n";
        out << "<td>" << record.oldValue << "</td>\n";
        out << "<td>" << record.newValue << "</td>\n";
        out << "<td>" << record.description << "</td>\n";
        out << "</tr>\n";
    }
    
    out << "</tbody>\n</table>\n";
    out << "</body>\n</html>\n";
    
    return true;
}

QStringList DataExporter::getAvailableFormats() const
{
    return {"CSV", "JSON", "XML", "PDF", "Excel", "HTML"};
}

QString DataExporter::getFormatExtension(ExportFormat format) const
{
    switch (format) {
        case ExportFormat::CSV: return ".csv";
        case ExportFormat::JSON: return ".json";
        case ExportFormat::XML: return ".xml";
        case ExportFormat::PDF: return ".pdf";
        case ExportFormat::EXCEL: return ".xlsx";
        case ExportFormat::HTML: return ".html";
        default: return ".txt";
    }
}

QString DataExporter::getFormatDescription(ExportFormat format) const
{
    switch (format) {
        case ExportFormat::CSV: return "Comma Separated Values";
        case ExportFormat::JSON: return "JavaScript Object Notation";
        case ExportFormat::XML: return "Extensible Markup Language";
        case ExportFormat::PDF: return "Portable Document Format";
        case ExportFormat::EXCEL: return "Microsoft Excel";
        case ExportFormat::HTML: return "HyperText Markup Language";
        default: return "Unknown Format";
    }
}

bool DataExporter::validateExportConfig(const ExportConfig &config, QString &errorMessage)
{
    if (config.outputPath.isEmpty()) {
        errorMessage = "Output path cannot be empty";
        return false;
    }
    
    QDir outputDir = QFileInfo(config.outputPath).dir();
    if (!outputDir.exists()) {
        errorMessage = "Output directory does not exist";
        return false;
    }
    
    if (config.filter.maxRecords == 0) {
        errorMessage = "Maximum records cannot be zero";
        return false;
    }
    
    if (config.encryptOutput && config.encryptionPassword.isEmpty()) {
        errorMessage = "Encryption password required when encryption is enabled";
        return false;
    }
    
    return true;
}

QVector<AuditLogger::AuditRecord> DataExporter::filterRecords(const QVector<AuditLogger::AuditRecord> &records, 
                                                             const ExportFilter &filter)
{
    QVector<AuditLogger::AuditRecord> filtered;
    
    for (const auto &record : records) {
        // Date range filter
        if (filter.startDate.isValid() && record.timestamp < filter.startDate) continue;
        if (filter.endDate.isValid() && record.timestamp > filter.endDate) continue;
        
        // User filter
        if (!filter.userIds.isEmpty() && !filter.userIds.contains(record.userId)) continue;
        
        // Action filter
        if (!filter.actions.isEmpty()) {
            QString actionStr = AuditLogger::actionToString(record.action);
            if (!filter.actions.contains(actionStr)) continue;
        }
        
        // Table name filter
        if (!filter.tableNames.isEmpty() && !filter.tableNames.contains(record.tableName)) continue;
        
        // Search text filter
        if (!filter.searchText.isEmpty()) {
            QString searchLower = filter.searchText.toLower();
            bool found = record.userId.toLower().contains(searchLower) ||
                        record.tableName.toLower().contains(searchLower) ||
                        record.description.toLower().contains(searchLower) ||
                        record.oldValue.toLower().contains(searchLower) ||
                        record.newValue.toLower().contains(searchLower);
            if (!found) continue;
        }
        
        filtered.append(record);
        
        // Max records limit
        if (filter.maxRecords > 0 && filtered.size() >= filter.maxRecords) {
            break;
        }
    }
    
    return filtered;
}

QString DataExporter::generateExportId() const
{
    return QUuid::createUuid().toString(QUuid::WithoutBraces);
}

QString DataExporter::escapeCSVField(const QString &field) const
{
    QString escaped = field;
    if (escaped.contains(',') || escaped.contains('"') || escaped.contains('\n')) {
        escaped.replace('"', "\"\"");
        escaped = '"' + escaped + '"';
    }
    return escaped;
}

QString DataExporter::formatDateTime(const QDateTime &dateTime) const
{
    return dateTime.toString("yyyy-MM-dd hh:mm:ss");
}

QJsonObject DataExporter::recordToJson(const AuditLogger::AuditRecord &record) const
{
    QJsonObject obj;
    obj["timestamp"] = formatDateTime(record.timestamp);
    obj["user_id"] = record.userId;
    obj["action"] = AuditLogger::actionToString(record.action);
    obj["table_name"] = record.tableName;
    obj["record_id"] = record.recordId;
    obj["old_value"] = record.oldValue;
    obj["new_value"] = record.newValue;
    obj["description"] = record.description;
    return obj;
}

void DataExporter::updateProgress(int percentage)
{
    m_currentProgress = percentage;
    emit exportProgress(m_currentExportId, percentage);
}

void DataExporter::setStatus(const QString &status)
{
    m_currentStatus = status;
}

void DataExporter::onScheduledExportTriggered()
{
    // Get the timer that triggered this slot
    QTimer *timer = qobject_cast<QTimer*>(sender());
    if (!timer) {
        return;
    }
    
    // Find the export ID for this timer
    QString exportId;
    for (auto it = m_scheduledExports.begin(); it != m_scheduledExports.end(); ++it) {
        if (it.value() == timer) {
            exportId = it.key();
            break;
        }
    }
    
    if (exportId.isEmpty()) {
        return;
    }
    
    // Get the export configuration
    if (!m_scheduledConfigs.contains(exportId)) {
        return;
    }
    
    ExportConfig config = m_scheduledConfigs[exportId];
    
    // Execute the scheduled export
    bool success = exportData(config);
    
    // Clean up the scheduled export
    m_scheduledExports.remove(exportId);
    m_scheduledConfigs.remove(exportId);
    timer->deleteLater();
    
    // Emit completion signal
    emit exportCompleted(exportId, success, config.outputPath);
}

bool DataExporter::compressFile(const QString &filePath)
{
    // Simple compression using gzip (requires system gzip command)
    QProcess process;
    process.start("gzip", QStringList() << filePath);
    process.waitForFinished();
    return process.exitCode() == 0;
}

bool DataExporter::encryptFile(const QString &filePath, const QString &password)
{
    // Simple encryption using openssl (requires system openssl command)
    QProcess process;
    QStringList args;
    args << "enc" << "-aes-256-cbc" << "-salt" << "-in" << filePath 
         << "-out" << filePath + ".enc" << "-pass" << "pass:" + password;
    
    process.start("openssl", args);
    process.waitForFinished();
    
    if (process.exitCode() == 0) {
        QFile::remove(filePath); // Remove original file
        QFile::rename(filePath + ".enc", filePath); // Rename encrypted file
        return true;
    }
    
    return false;
}

int DataExporter::getExportProgress() const
{
    return m_currentProgress;
}

QString DataExporter::getCurrentExportStatus() const
{
    return m_currentStatus;
}